-- KillStats v0.5 by TotalMrak


-- The MIT License 

-- Copyright (c) 2009 Michael Rakita aka TotalMrak

-- Permission is hereby granted, free of charge, to any person obtaining a copy
-- of this software and associated documentation files (the "Software"), to deal
-- in the Software without restriction, including without limitation the rights
-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-- copies of the Software, and to permit persons to whom the Software is
-- furnished to do so, subject to the following conditions: 

-- The above copyright notice and this permission notice shall be included in
-- all copies or substantial portions of the Software. 

-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-- THE SOFTWARE. 


-- Globals
g_KillStats_version = "v0.5";
g_KillStats_totalKills = 0;
g_KillStats_mobs = {};
g_KillStats_settings = {};


function KillStats_OnLoad(this)
  this:RegisterEvent("VARIABLES_LOADED");
  this:RegisterEvent("UNIT_HEALTH");
  this:RegisterEvent("UNIT_TARGET_CHANGED");
  this:RegisterEvent("UNIT_NAME_UPDATE");
  this:RegisterEvent("LOADING_END");
  SaveVariables("g_KillStats");
  SaveVariables("g_KillStats_settings");
end


function KillStats_OnEvent(this, event, arg1, arg2)
  if (event == "VARIABLES_LOADED") then
    KillStats_OnVariablesLoaded();
  elseif (event == "UNIT_HEALTH") then
    KillStats_OnUnitHealth(arg1, arg2);
  elseif (event == "UNIT_TARGET_CHANGED") then
    KillStats_OnUnitTargetChanged(arg1);
  elseif (event == "LOADING_END") then
    KillStats_OnLoadingEnd();
  elseif (event == "UNIT_NAME_UPDATE") then
    KillStats_OnUnitNameUpdate(arg1);
  end
end


function KillStats_OnVariablesLoaded()
  if (g_KillStats == nil) then
    g_KillStats = {};
  end
end


function KillStats_OnUnitHealth(unit, health)
  if (unit == "target" and UnitExists(unit)) then
    if (not UnitIsPlayer(unit)) then
      KillStats_UpdateHP(unit);
    end
    if (health < 5 or UnitHealth(unit) == 0) then
      -- The target is dead. Store info about the target
      KillStats_UnitDead(unit);
    end
  end
end


function KillStats_OnUnitTargetChanged(arg1)
  if (arg1 == "player" and UnitExists("target")) then
    if (not UnitIsPlayer("target")) then
      KillStats_UpdateHP("target");
    end
  end
end


function KillStats_OnShow(this)
  if (g_KillStats_settings.x ~= nil and g_KillStats_settings.y ~= nil) then
    this:ClearAllAnchors();
    this:SetAnchor("TOPLEFT", "TOPLEFT", UIParent, g_KillStats_settings.x, g_KillStats_settings.y);
  end
--  local screenH = GetScreenHeight();
--  local screenW = GetScreenWidth();
--  local scale = this:GetRealScale();
--  if ((this:GetTop() + 20 > screenH / scale) or (this:GetLeft() + 100 > screenW / scale)) then
--    this:ClearAllAnchors();
--    this:SetAnchor("TOPLEFT", "TOPLEFT", UIParent, 160, 50);
--  end
  KillStats_OnLoadingEnd();
end


function KillStats_OnLoadingEnd()
  local playerName = UnitName("player");
  if (playerName ~= nil) then
    g_KillStats_totalKills = CalculateTotalKills(playerName);
    KillStats_UpdateUI();
  end
end


function KillStats_OnUnitNameUpdate(unit)
  if (unit == "player") then
    g_KillStats_totalKills = CalculateTotalKills(UnitName("player"));
    KillStats_UpdateUI();
  end
end


function KillStats_StopMoving(this, key)
  local ksframe = this:GetParent();
  ksframe:StopMovingOrSizing();
  local x, y = ksframe:GetPos();
  local scale = ksframe:GetRealScale();
  g_KillStats_settings.x = x / scale;
  g_KillStats_settings.y = y / scale;
end


function KillStats_UnitDead(unit)
  local name = UnitName(unit);
  if (name == nil) then
    KillStats_PrintToChat("Can't store kill because target name is nil!");
    return;
  end
  local level = UnitLevel(unit);
  local sex = UnitSex(unit);
  local maxHealth = 0;
  local isPlayer = UnitIsPlayer(unit);
  if (isPlayer) then
    maxHealth = UnitMaxHealth(unit);
    local mClass, sClass = UnitClass("target");
    local mLevel, sLevel = UnitLevel("target");
    KillStats_StoreKill(name, level, sex, maxHealth, isPlayer, mClass, sLevel, sClass);
--    if (sLevel == nil or sLevel == 0) then
--      KillStats_PrintToChat(name .. " (" .. mClass .. " lvl " .. mLevel .. ", " .. maxHealth .. " HP) killed.");
--    else
--      KillStats_PrintToChat(name .. " (" .. mClass .. " lvl " .. mLevel .. "/" .. sClass .. " lvl " .. sLevel .. ", " .. maxHealth .. " HP) killed.");
--    end
  else
    maxHealth = KillStats_GetHP(name);
    KillStats_StoreKill(name, level, sex, maxHealth, isPlayer, nil, nil, nil);
--    KillStats_PrintToChat(name .. "  (lvl " .. level .. ", " .. maxHealth .. " HP) killed.");
  end
end


function KillStats_StoreKill(name, level, sex, maxHealth, isPlayer, class, sLevel, sClass)
  local playerName = UnitName("player");
  if (playerName == nil) then
    KillStats_PrintToChat("Can't store kill because player name is nil!");
    return;
  end

  KillStats_CreateEntry(name, playerName);
  g_KillStats[playerName].Monsters[name].Kills = g_KillStats[playerName].Monsters[name].Kills + 1;
  if (g_KillStats[playerName].Monsters[name].MinLevel > level) then
    g_KillStats[playerName].Monsters[name].MinLevel = level;
  end
  if (g_KillStats[playerName].Monsters[name].MaxLevel < level) then
    g_KillStats[playerName].Monsters[name].MaxLevel = level;
  end
  if (g_KillStats[playerName].Monsters[name].HP < maxHealth) then
    g_KillStats[playerName].Monsters[name].HP = maxHealth;
  end
  g_KillStats[playerName].Monsters[name].Sex = sex;
  g_KillStats[playerName].Monsters[name].IsPlayer = isPlayer;
  if (isPlayer) then
    g_KillStats[playerName].Monsters[name].Class = class;
    if (sLevel ~= nil and sLevel > 0) then
      g_KillStats[playerName].Monsters[name].SClass = sClass;
      if (g_KillStats[playerName].Monsters[name].MinSLevel > sLevel) then
        g_KillStats[playerName].Monsters[name].MinSLevel = sLevel;
      end
      if (g_KillStats[playerName].Monsters[name].MaxSLevel < sLevel) then
        g_KillStats[playerName].Monsters[name].MaxSLevel = sLevel;
      end
    end
  end

  -- Calculate total kills for the player
  if (g_KillStats_totalKills == 0) then
    g_KillStats_totalKills = CalculateTotalKills(playerName);
  else
    g_KillStats_totalKills = g_KillStats_totalKills + 1;
  end
  KillStats_UpdateUI();
end


function KillStats_CreateEntry(name, playerName)
  if (g_KillStats[playerName] == nil) then
    g_KillStats[playerName] = {};
  end
  if (g_KillStats[playerName].Monsters == nil) then
    g_KillStats[playerName].Monsters = {};
  end
  if (g_KillStats[playerName].Monsters[name] == nil) then
    g_KillStats[playerName].Monsters[name] = {};
    g_KillStats[playerName].Monsters[name].HP = 0;
    g_KillStats[playerName].Monsters[name].Kills = 0;
    g_KillStats[playerName].Monsters[name].MinLevel = 30000;
    g_KillStats[playerName].Monsters[name].MaxLevel = 0;
    g_KillStats[playerName].Monsters[name].MinSLevel = 30000;
    g_KillStats[playerName].Monsters[name].MaxSLevel = 0;
  end
end


function KillStats_UpdateHP(unit)
  local name = UnitName(unit);
  if (name ~= nil) then
    if (g_KillStats_mobs[name] == nil) then
      g_KillStats_mobs[name] = {};
      g_KillStats_mobs[name].HP = 0;
    end
    local maxHP = 0;
    if (UnitIsPlayer(unit)) then
      maxHP = UnitMaxHealth(unit);
    else
      maxHP = UnitChangeHealth(unit);
    end
    if (g_KillStats_mobs[name].HP < maxHP) then
      g_KillStats_mobs[name].HP = maxHP;
    end
  end
end


function KillStats_GetHP(name)
  if (name == nil or g_KillStats_mobs[name] == nil) then
    return 0;
  end
  return g_KillStats_mobs[name].HP;
end


function KillStats_UpdateUI()
  KillStatsText:SetText(g_KillStats_totalKills .. " kills");
end


function CalculateTotalKills(playerName)
  if (playerName == nil) then
    return 0;
  end
  if (g_KillStats == nil or g_KillStats[playerName] == nil) then
    return 0;
  end
  local kills = 0;
  for k,v in pairs(g_KillStats[playerName].Monsters) do
    kills = kills + v.Kills;
  end
  return kills;
end


function KillStats_PrintToChat(msg)
  DEFAULT_CHAT_FRAME:AddMessage("|cffffffffKillStats|r: " .. msg, 0.5, 0.7, 0.1);
end


KillStatsPanelList_Max = 7;
KillStatsPanelList_ScrollValue = 0;
KillStatsPanelList_Show = 0;
KillStatsPanelList_Sort = 0;

function KillStatsPanelFrame_Init()
  KillStatsPanel_PlayerName:SetText(UnitName("player"));
  KillStatsPanel_Version:SetText("KillStats " .. g_KillStats_version .. " by TotalMrak");
  KillStatsPanelList_Init();
end


function KillStatsPanel_ScrollBar_OnValueChanged(this, arg1)
  UIPanelScrollBar_OnValueChanged(this, arg1);
  KillStatsPanelList_ScrollValue = this:GetValue();
  KillStatsPanelList_Update();
end


function KillStatsPanel_ShowDropDown_OnLoad(this)
  UIDropDownMenu_Initialize(this, KillStatsPanel_ShowDropDown_Show);
  UIDropDownMenu_SetSelectedID(KillStatsPanel_ShowDropDown, 1);
end


function KillStatsPanel_ShowDropDown_Show(this)
  local info;
  info = {};
  info.text = "Monsters";
  info.func = KillStatsPanel_ShowDropDown_Click;
  info.arg1 = 0;
  UIDropDownMenu_AddButton(info);
  info = {};
  info.text = "Players";
  info.func = KillStatsPanel_ShowDropDown_Click;
  info.arg1 = 1;
  UIDropDownMenu_AddButton(info);
end


function KillStatsPanel_ShowDropDown_Click(button)
  UIDropDownMenu_SetSelectedID(KillStatsPanel_ShowDropDown, button:GetID());
  KillStatsPanelList_Show = button.arg1;
  KillStatsPanelList_ScrollValue = 0;
  KillStatsPanelList_Init();
end


function KillStatsPanel_SortDropDown_OnLoad(this)
  UIDropDownMenu_Initialize(this, KillStatsPanel_SortDropDown_Show);
  UIDropDownMenu_SetSelectedID(KillStatsPanel_SortDropDown, 1);
end


function KillStatsPanel_SortDropDown_Show(this)
  local info;
  info = {};
  info.text = "Name";
  info.func = KillStatsPanel_SortDropDown_Click;
  info.arg1 = 0;
  UIDropDownMenu_AddButton(info);
  info = {};
  info.text = "Level";
  info.func = KillStatsPanel_SortDropDown_Click;
  info.arg1 = 1;
  UIDropDownMenu_AddButton(info);
  info = {};
  info.text = "Kills";
  info.func = KillStatsPanel_SortDropDown_Click;
  info.arg1 = 2;
  UIDropDownMenu_AddButton(info);
end


function KillStatsPanel_SortDropDown_Click(button)
  UIDropDownMenu_SetSelectedID(KillStatsPanel_SortDropDown, button:GetID());
  KillStatsPanelList_Sort = button.arg1;
  KillStatsPanelList_ScrollValue = 0;
  KillStatsPanelList_Init();
end


function KillStatsPanelList_OnMouseWheel(this, delta)
  if ( delta > 0 ) then
    UIPanelScrollBarTemplate_Scroll(KillStatsPanel_ScrollBar, "UP");
  else
    UIPanelScrollBarTemplate_Scroll(KillStatsPanel_ScrollBar, "DOWN");
  end
end


function KillStatsPanelList_Init()
  KillStats_PrepareSortedKillList();
  if (g_KillStats_sortedKillListCount > KillStatsPanelList_Max) then
    KillStatsPanel_ScrollBar:Show();
    KillStatsPanel_ScrollBar:SetMinMaxValues(0, g_KillStats_sortedKillListCount - KillStatsPanelList_Max);
  else
    KillStatsPanel_ScrollBar:Hide();
    KillStatsPanel_ScrollBar:SetMinMaxValues(0, 0);
    KillStatsPanelList_ScrollValue = 0;
  end
  KillStatsPanelList_Update();
end


function KillStatsPanelList_Update()
  if (g_KillStats_sortedKillList == nil) then
    KillStats_PrepareSortedKillList();
  end
  for i = 1, KillStatsPanelList_Max do
    local texture = getglobal("KillStatsPanel_Item" .. i .. "_Texture");
    local name = getglobal("KillStatsPanel_Item" .. i .. "_Name");
    local level = getglobal("KillStatsPanel_Item" .. i .. "_Level");
    local kills = getglobal("KillStatsPanel_Item" .. i .. "_Kills");
    local stats = g_KillStats_sortedKillList[i + KillStatsPanelList_ScrollValue];
    if (stats ~= nil) then
      texture:Show();
      name:SetText(stats.Name);
      if (stats.IsPlayer) then
        name:SetColor(1, 1, 1);
      elseif (stats.Sex == 2) then
        name:SetColor(1, 0.5, 0.1);
      elseif (stats.Sex > 2) then
        name:SetColor(1, 0.1, 0.1);
      else
        name:SetColor(1, 0.82, 0);
      end
      local info = KillStats_GetInfoString(stats);
      level:SetText(info);
      kills:SetText(stats.Kills .. " killed");
    else
      texture:Hide();
      name:SetText("");
      level:SetText("");
      kills:SetText("");
    end
  end
end


function KillStats_GetInfoString(stats)
  local info  = "(lvl " .. stats.Level .. ", " .. stats.HP .. " HP)";
  if (stats.IsPlayer) then
    if (stats.Class ~= nil and stats.SLevel ~= nil and stats.SClass ~= nil) then
      info = "(" .. stats.Class .. " lvl " .. stats.Level .. "/" .. stats.SClass .. " lvl " .. stats.SLevel .. ", " .. stats.HP .. " HP)";
    elseif (stats.Class ~= nil) then
      info = "(" .. stats.Class .. " lvl " .. stats.Level .. ", " .. stats.HP .. " HP)";
    end
  elseif (stats.Sex == 2) then
    info  = "(Elite, lvl " .. stats.Level .. ", " .. stats.HP .. " HP)";
  elseif (stats.Sex ~= nil and stats.Sex > 2) then
    info  = "(Boss, lvl " .. stats.Level .. ", " .. stats.HP .. " HP)";
  end
  return info;
end


function KillStats_PrepareSortedKillList()
  g_KillStats_sortedKillList = {};
  g_KillStats_sortedKillListCount = 0;
  local playerName = UnitName("player");
  if (g_KillStats[playerName] == nil or g_KillStats[playerName].Monsters == nil) then
    return;
  end
  local index = 1;
  for k,v in pairs(g_KillStats[playerName].Monsters) do
    if ((v.IsPlayer and KillStatsPanelList_Show == 1) or (not v.IsPlayer and KillStatsPanelList_Show == 0)) then
      if (v.Kills > 0) then
        g_KillStats_sortedKillList[index] = {};
        g_KillStats_sortedKillList[index].Name = k;
        g_KillStats_sortedKillList[index].MaxLevel = v.MaxLevel;
        if (v.MinLevel == v.MaxLevel) then
          g_KillStats_sortedKillList[index].Level = v.MinLevel;
        else
          g_KillStats_sortedKillList[index].Level = v.MinLevel .. "-" .. v.MaxLevel;
        end
        if (v.MinSLevel == v.MaxSLevel) then
          g_KillStats_sortedKillList[index].SLevel = v.MinSLevel;
        else
          g_KillStats_sortedKillList[index].SLevel = v.MinSLevel .. "-" .. v.MaxSLevel;
        end
        g_KillStats_sortedKillList[index].Kills = v.Kills;
        g_KillStats_sortedKillList[index].HP = v.HP;
        g_KillStats_sortedKillList[index].Sex = v.Sex;
        g_KillStats_sortedKillList[index].IsPlayer = v.IsPlayer;
        g_KillStats_sortedKillList[index].Class = v.Class;
        g_KillStats_sortedKillList[index].SClass = v.SClass;
        index = index + 1;
      end
    end
  end
  table.sort(g_KillStats_sortedKillList, KillStatsItem_Compare);
  g_KillStats_sortedKillListCount = index - 1;
end


function KillStatsItem_Compare(item1, item2)
  if (KillStatsPanelList_Sort == 2) then
    -- Sort by kills
    if (item1.Kills == item2.Kills) then
      return item1.Name < item2.Name;
    end
    return item1.Kills > item2.Kills;
  elseif (KillStatsPanelList_Sort == 1) then
    -- Sort by level
    local level1 = item1.MaxLevel;
    if (item1.Sex == 2) then
      level1 = level1 + 10000;
    elseif (item1.Sex ~= nil and item1.Sex > 2) then
      level1 = level1 + 100000;
    end
    local level2 = item2.MaxLevel;
    if (item2.Sex == 2) then
      level2 = level2 + 10000;
    elseif (item2.Sex ~= nil and item2.Sex > 2) then
      level2 = level2 + 100000;
    end
    if (level1 == level2) then
      return item1.Name < item2.Name;
    end
    return level1 > level2;
  else
    -- Sort by name
    return item1.Name < item2.Name;
  end
end
